/**
 * File: FillCurtainGraph.java
 * Created by: mhaimel
 * Created on: Apr 20, 2010
 * CVS:  $Id: FillCurtainGraph.java 1.0 Apr 20, 2010 4:44:40 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.model.graph.curtain.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import uk.ac.ebi.curtain.model.graph.Arc;
import uk.ac.ebi.curtain.model.graph.curtain.ContigInfo;
import uk.ac.ebi.curtain.model.graph.curtain.CurtainGraph;
import uk.ac.ebi.curtain.model.graph.curtain.ReadWrapper;
import uk.ac.ebi.curtain.utils.StatisticHelper;
import uk.ac.ebi.curtain.utils.match.MatchDetails;
import uk.ac.ebi.curtain.utils.match.sam.AlternativeMatch;
import uk.ac.ebi.velvet.model.Strand;

/**
 * @author mhaimel
 *
 */
public class FillCurtainGraph {
	
	private Integer category;
	private CurtainGraph cGraph;
	private Map<String, Long> read2ctg = new HashMap<String, Long>(); 
	private Map<String, MatchDetails> read2details = new HashMap<String, MatchDetails>(); 
	private final int insLen;
	private boolean distanceInfo = false;;

	public FillCurtainGraph(Integer category, CurtainGraph cGraph, Integer insLen) {
		this.category = category;
		this.cGraph = cGraph;
		this.insLen = insLen;
	}
	
	public Arc<ContigInfo, ReadWrapper> doMap(Long aId, AlternativeMatch a, Long bId, AlternativeMatch b){
		
		Long aLen = (long) a.getAlignLength();
		Long bLen = (long) b.getAlignLength();
		
		boolean aFWD = Strand.FWD.equals(a.getAlignOrientation());
		boolean bFWD = Strand.FWD.equals(b.getAlignOrientation());
		
		if(aId.equals(bId)){
			throw new IllegalStateException("Perfect matches should be handled before!!!");
			// TODO already handled in BAM mapper
//			// same sequence
//			ContigInfo val = cGraph.getNode(aId).getValue();
//			val.addSequence(category, aLen+bLen);// 2x51
//			val.incrementFull(category);
		} 
		// mapping
		ContigInfo ac = cGraph.getNode(aId).getValue();
		ContigInfo bc = cGraph.getNode(bId).getValue();

		ac.addSequence(category, aLen);// 1x51
		bc.addSequence(category, bLen);// 1x51
		// if(!details.getFirstInPair()){ 
		if(a.getReadOrientation().equals(Strand.REV)){
			ContigInfo c = ac;
			ac = bc;
			bc = c;
			Long idc = aId;
			aId = bId;
			bId = idc;
			boolean x = aFWD;
			aFWD = bFWD;
			bFWD = x;
			AlternativeMatch m = a;
			a = b;
			b = m;
		}
		int gap = 0;
		aLen = (long) a.getAlignLength();
		bLen = (long) b.getAlignLength();
		gap += aFWD ?ac.getLength() - a.getReferenceLocation():a.getReferenceLocation();
		gap += bFWD ?bc.getLength() - b.getReferenceLocation():b.getReferenceLocation();
		if(aFWD){
			gap +=1; // 1 based
		}else {
			gap += aLen+1;						
		}
		if(bFWD) {
			gap += 1;
		} else {
			gap += bLen+1;
		}
		
		gap = insLen - gap;
		Arc<ContigInfo, ReadWrapper> arc = null;
		if((aFWD && !bFWD)){
			arc = cGraph.createArcEnd2Start(cGraph.getNode(aId), cGraph.getNode(bId));
		} else if(!aFWD && bFWD){
			arc = cGraph.createArcEnd2Start(cGraph.getNode(bId), cGraph.getNode(aId));
		} else if(aFWD && bFWD){
			arc = cGraph.createArcEnd2End(cGraph.getNode(aId), cGraph.getNode(bId));
		} else {
			arc = cGraph.createArcStart2Start(cGraph.getNode(aId), cGraph.getNode(bId));								
		}
		List<Integer> qual = new ArrayList<Integer>();
		if(a.getQualitiesLength() > 0){
			qual.add(a.getQualities(0));
		}
		if(b.getQualitiesLength() > 0){
			qual.add(b.getQualities(0));
		}
		double weight = StatisticHelper.avg(qual).doubleValue();
		if(distanceInfo){
			arc.getValue().addRead(category, gap, weight);
		}else {
			arc.getValue().addRead(category, weight);
		}
		if(a.getRepeatMarker() || b.getRepeatMarker()){
			arc.getValue().getInfo(category).incrementRepeatMarker();
		}
		return arc;
	}
	
	public void mapEntry(Long id, MatchDetails details) {
		Long otherId = read2ctg.remove(details.getReadId());
		// paired entry already parsed
		if(null == otherId){

			read2details.put(details.getReadId(), details);
			read2ctg.put(details.getReadId(), id);
			return;
		} 
		MatchDetails otherDetails = read2details.remove(details.getReadId());
		if(details.getMapped() && details.getOtherMapped()){
			doMap(id, details.getAsAlternativeMatch(), otherId, otherDetails.getAsAlternativeMatch());
		} 
	}
	
	

	public void setDistanceInfo(boolean b) {
		this.distanceInfo  = b;
	}
}
